package org.jianfa.learn.transfer.factory;

import com.alibaba.druid.util.StringUtils;
import org.jianfa.learn.transfer.annotation.Autowired;
import org.jianfa.learn.transfer.annotation.Service;
import org.jianfa.learn.transfer.annotation.Transactional;
import org.jianfa.learn.transfer.utils.MethodUtil;
import org.jianfa.learn.transfer.utils.TransactionUtil;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class AnnotationBeanFactory {

    private static Map<String,Object> mapById = new HashMap<>();  // 存储对象
    private static Map<Class, Object> mapByType = new HashMap<>(); // 存储对象

    static {
        try {
            String packageName = "org.jianfa.learn.transfer";
            traversePackage(packageName);
            completeClass();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 遍历packageName包下的所有.class文件
     *
     * @param packageName
     * @throws Exception
     */
    public static void traversePackage(String packageName) throws Exception{

        URL url = AnnotationBeanFactory.class.getClassLoader().getResource(packageName.replace(".", "/"));
        String packagePath = url.getPath();

        File[] files = new File(packagePath).listFiles(new FileFilter() {

            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
            }
        });

        for (File file : files) {

            if (file.isFile()) {
                String fileName = file.getName();
                String className = fileName.substring(0, fileName.indexOf(".class"));
                String absoluteClassName = packageName + "." + className;
                initializeClass(absoluteClassName);
            }

            if (file.isDirectory()) {
                String directoryName = file.getName();
                String subPackageName = packageName + "." + directoryName;
                traversePackage(subPackageName);
            }
        }
    }

    /**
     * 初始化类
     *
     * @param className
     * @throws Exception
     */
    public static void initializeClass(String className) throws Exception{
        Class clazz = Class.forName(className);
        boolean isServicePresent = clazz.isAnnotationPresent(Service.class);
        if (isServicePresent) {
            Service service = (Service) clazz.getDeclaredAnnotation(Service.class);
            String serviceValue = service.value();
            if (StringUtils.isEmpty(serviceValue)) {
                serviceValue = className.substring(className.lastIndexOf(".") + 1);
            }

            Object object = clazz.newInstance();
            mapById.put(serviceValue, object);
            mapByType.put(clazz, object);
            System.out.println(className  + " construct with id " + serviceValue);
        }
    }

    /**
     * 1. 注入依赖
     * 2. 判断是否需要开启事务
     *
     * @throws Exception
     */
    public static void completeClass() throws Exception {
        for (Map.Entry<Class, Object> mapByTypeEntry : mapByType.entrySet()) {
            Class objectClass = mapByTypeEntry.getKey();
            Object object = mapByTypeEntry.getValue();

            Method[] methods = objectClass.getMethods();
            for (Method method : methods) {

                //注入依赖
                boolean isAutowired = method.isAnnotationPresent(Autowired.class);
                if (isAutowired) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    Object[] parameterObjects = new Object[parameterTypes.length];
                    for (int i=0; i<parameterTypes.length; i++) {
                        Class parameterType = parameterTypes[i];
                        parameterObjects[i] = getBean(parameterType);
                    }

                    method.invoke(object, parameterObjects);
                }

                //判断是否需要开启事务
                boolean isTransactional = method.isAnnotationPresent(Transactional.class);
                if (isTransactional) {
                    StringBuilder sb = new StringBuilder();
                    TransactionUtil.transactionClass.put(objectClass, Boolean.TRUE);
                    String methodIdentifier = MethodUtil.getMethodIdentifier(method);
                    TransactionUtil.transactionMethod.put(methodIdentifier, Boolean.TRUE);
                }
            }
        }
    }

    /**
     * 通过类型获取bean
     *
     * @param clazz
     * @return
     */
    public static Object getBean(Class clazz) {
        int match = 0;
        Object object = null;
        for (Map.Entry<Class, Object> mapByTypeEntry : mapByType.entrySet()) {
            Class objectClass = mapByTypeEntry.getKey();
            if (clazz.isAssignableFrom(objectClass)) {
                match = match + 1;
                if (match > 1) {
                    throw new RuntimeException("匹配到多个实例。");
                }
                object = mapByTypeEntry.getValue();
                System.out.println(clazz.getName() + " is AssignableFrom " + objectClass.getName());
            }
        }
        return object;
    }

    /**
     * 通过id获取bean
     *
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        Object object = mapById.get(id);
        return object;
    }

}
